%option noyywrap
%option yylineno
%{
    /*
    * You will need to comment this line in lab5.
    */
    #define ONLY_FOR_LEX
    
    #ifdef ONLY_FOR_LEX
    #else
    #define YYSTYPE void *
    #include "parser.h"
    #endif

    #define YY_NO_UNPUT
    #define YY_NO_INPUT
    #include <string>

    #ifdef ONLY_FOR_LEX
    #include <ostream>
    #include <fstream>
    #include <cstring>
    #include <stdio.h>
    #include <stdlib.h>
    using namespace std;
    extern FILE *yyin; 
    extern FILE *yyout;
    int alignspace = 16;
    //int mylineno;
    string align(string str);
    void DEBUG_FOR_LAB4(std::string s){
	//std::string s2 = align(s);
        std::string DEBUG_INFO = "[DEBUG LAB4]: \t" + s + "\n";
        fputs(DEBUG_INFO.c_str(), yyout);
    }

string align(string str){
int st = 0, fn = 0;
int size = str.length();
for(int i = size - 1; i >= 0; i --){if(str[i] == '\t'){fn = i;break;}}
for(int i = fn - 1; i >= 0; i --){
	if(str[i] == '\t'){
		st = i + 1;
		int nowlen = fn - st;
		if(nowlen < alignspace){
			for(int j = 0; j < alignspace - nowlen; j ++){
			str.insert(fn, " ");
			}
		}
		//i = i + alignspace - nowlen;
		fn = st - 1;
	}
}
return str;
}

struct SymbolTable {
	char name[32];
	int value;
};
SymbolTable symboltable[1024];
int STpointer = 0;
int search(char* s){
	for(int i = 0; i < STpointer; i ++){
		if(strcmp(symboltable[i].name, s) == 0){
			return i;
		}
	}
	return -1;
}
    #endif
%}

DECIMIAL ([1-9][0-9]*|0)
OCTONARY (0[1-9][0-9]*)
HEXADECIMAL (0x[1-9][0-9]*|0x0)
ID [[:alpha:]_][[:alpha:][:digit:]_]*
EOL (\r\n|\n|\r)
WHITE [\t ]
LINECOMMENT \/\/[^\n]*
blockcommentbegin "/*"
blockcommentelement .|\n
blockcommentend "*/"
%x BLOCKCOMMENT

%%

"int" {
    /*
    * Questions: 
    *   Q1: Why we need to return INT in further labs?
    *   Q2: What is "INT" actually?
    */
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("INT\t\tint");
    #else
        return INT;
    #endif
}
"void" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("VOID\t\tvoid");
    #else
        return VOID;
    #endif 
}
"main" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("MAIN\t\tmain");
    #else
        return MAIN;
    #endif 
}
"if" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("IF\t\tif");
    #else
        return IF;
    #endif
};
"else" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("ELSE\t\telse");
    #else
        return ELSE;
    #endif
};
"while" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("WHILE\t\twhile");
    #else
        return WHILE;
    #endif
}
"return" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("RETURN\t\treturn");
    #else
        return RETURN;
    #endif
}

"=" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("ASSIGN\t\t=");
    #else
        return ASSIGN;
    #endif
}
"<" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("LESS\t\t<");
    #else
        return LESS;
    #endif
}
"==" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("EQ\t\t==");
    #else
        return EQ;
    #endif
}
"+" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("ADD\t\t+");
    #else
        return ADD;
    #endif
}
"-" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("SUB\t\t-");
    #else
        return SUB;
    #endif
}
"*" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("MUL\t\t*");
    #else
        return MUL;
    #endif
}
"/" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("DIV\t\t/");
    #else
        return DIV;
    #endif
}
";" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("SEMICOLON\t;");
	//yylineno--;
    #else
        return SEMICOLON;
    #endif
}
"(" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("LPAREN\t\t(");
    #else
        return LPAREN;
    #endif
}
")" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("RPAREN\t\t)");
    #else
    return RPAREN;
    #endif
}
"{" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("LBRACE\t\t{");
    #else
        return LBRACE;
    #endif
}
"}" {
    #ifdef ONLY_FOR_LEX
        DEBUG_FOR_LAB4("RBRACE\t\t}");
    #else
        return RBRACE;
    #endif
}

{EOL} yylineno++;
{WHITE}
{LINECOMMENT}
{blockcommentbegin} {BEGIN BLOCKCOMMENT;}
<BLOCKCOMMENT>{blockcommentelement}	{}
<BLOCKCOMMENT>{blockcommentend}	{BEGIN INITIAL;}
{ID} {
	#ifdef ONLY_FOR_LEX
	char str[32];
	//printf("here id!");
	strcpy(str, "ID\t\t");
	strcat(str, yytext);
	char lineno[10];	
	sprintf(lineno, "%d", yylineno);
	strcat(str, "\t");
	strcat(str, lineno);
	//DEBUG_FOR_LAB4("ID\tid");
	//add to symbol table
	int index = search(yytext);
	if(index < 0){
	strcpy(symboltable[STpointer].name, yytext);
	strcat(str, "\t");
	char stpointer[10];
	sprintf(stpointer, "%d", STpointer);
	strcat(str, stpointer);
	STpointer ++;
	}
	else{
	char indexstr[10];
	sprintf(indexstr, "%d", index);
	strcat(str, "\t");
	strcat(str, indexstr);
	}
	DEBUG_FOR_LAB4(str);
	#else
		return ID;
	#endif
}
{DECIMIAL} {
	#ifdef ONLY_FOR_LEX
	char str[32];
	//printf("here decimial!");
	strcpy(str, "DECIMIAL\t");
	strcat(str, yytext);
	char lineno[10];	
	sprintf(lineno, "%d", yylineno);
	strcat(str, "\t");
	strcat(str, lineno);
	DEBUG_FOR_LAB4(str);
	//DEBUG_FOR_LAB4("DECIMIAL\tdecimial");
	#else
	return DECIMIAL;
	#endif
}
{OCTONARY} {
	#ifdef ONLY_FOR_LEX
	char str[32];
	strcpy(str, "OCTONARY\t");
	int o_num = strtol(yytext, NULL, 8);
	char o_num_s[10];
	sprintf(o_num_s, "%d", o_num);
	strcat(str, o_num_s);
	char lineno[10];	
	sprintf(lineno, "%d", yylineno);
	strcat(str, "\t");
	strcat(str, lineno);
	DEBUG_FOR_LAB4(str);
	//DEBUG_FOR_LAB4("DECIMIAL\t" + yytext);
	#else
	return OCTONARY;
	#endif
}
{HEXADECIMAL} {
	#ifdef ONLY_FOR_LEX
	char str[32];
	strcpy(str, "HEXADECIMAL\t");
	int o_num = strtol(yytext, NULL, 16);
	char o_num_s[10];
	sprintf(o_num_s, "%d", o_num);
	strcat(str, o_num_s);
	char lineno[10];	
	sprintf(lineno, "%d", yylineno);
	strcat(str, "\t");
	strcat(str, lineno);
	DEBUG_FOR_LAB4(str);
	//DEBUG_FOR_LAB4("DECIMIAL\t" + yytext);
	#else
	return HEXADECIMAL;
	#endif
}
%%

#ifdef ONLY_FOR_LEX
int main(int argc, char **argv){
    if(argc != 5){
        fprintf(stderr, "Argument Not Enough");
        exit(EXIT_FAILURE);
    }

    if(!(yyin = fopen(argv[1], "r"))){
        fprintf(stderr, "No such file or directory: %s", argv[1]);
        exit(EXIT_FAILURE);
    }

    if(!(yyout = fopen(argv[3], "w"))){
        fprintf(stderr, "No such file or directory: %s", argv[3]);
        exit(EXIT_FAILURE);
    }
    yylineno = 1;
    //mylineno = 1;
    yylex();
    return 0;
}
#endif
